package cn.tp.ksoft.civil_driver.view.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;

import java.lang.ref.WeakReference;

/**
 * Created by zhusong on 16/4/28.
 */
public class PinnedRecycleView extends RecyclerView {

    private View mHeaderView;
    private int mNextPinnedPosition;
    private int mCurrentPinnedIndex = -1;

    private PinnedWrapAdapter mAdapter;
    private UpdateTypeObserver mDataObserver = new UpdateTypeObserver();

    public PinnedRecycleView(Context context) {
        this(context, null);
    }

    public PinnedRecycleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PinnedRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        addOnScrollListener(new ScrollListener());
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (null != mHeaderView) {
            drawChild(canvas, mHeaderView, getDrawingTime());
        }
    }

    @Override
    public void setLayoutManager(LayoutManager layout) {
        if (layout != null && layout instanceof StaggeredGridLayoutManager) {
            throw new IllegalArgumentException("Don't support StaggeredGridLayoutManager");
        }
        super.setLayoutManager(layout);
    }

    @Override
    @Deprecated
    public void setAdapter(Adapter adapter) {
        if (isInEditMode()) {
            super.setAdapter(adapter);
            return;
        }
        throw new IllegalArgumentException("Using setAdapter(pinnedAdapter,contentAdapter) instead");
    }

    public final PinnedWrapAdapter setAdapter(Adapter pinnedAdapter, Adapter contentAdapter, SparseIntArray sectionCountArr) {
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mDataObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }
        mAdapter = new PinnedWrapAdapter(this, pinnedAdapter, contentAdapter, sectionCountArr);
        mAdapter.registerAdapterDataObserver(mDataObserver);
        super.setAdapter(mAdapter);
        return mAdapter;
    }


    public static class PinnedWrapAdapter<VH extends ViewHolder> extends Adapter {
        private final int INVALID_POSITION = -1;
        private static final int TYPE_ITEM = Short.MIN_VALUE << 1;
        private static final int TYPE_PINNED = Short.MIN_VALUE << 2;

        private WeakReference<PinnedRecycleView> mWeakRef;
        private Adapter mPinnedAdapter;
        private Adapter mContentAdapter;

        private SparseIntArray mTypeArr = new SparseIntArray();
        private SparseIntArray mPinnedArr = new SparseIntArray();
        private SparseIntArray mContentArr = new SparseIntArray();
        private SparseIntArray mSectionCountArr;

        private PinnedWrapAdapter(){}

        private PinnedWrapAdapter(PinnedRecycleView mRecycleView, Adapter pinnedAdapter, Adapter contentAdapter, SparseIntArray sectionCountArr) {
            mWeakRef = new WeakReference<PinnedRecycleView>(mRecycleView);
            mPinnedAdapter = pinnedAdapter;
            mContentAdapter = contentAdapter;
            updateSectionCountArr(sectionCountArr);
        }

        public void updateSectionCountArr(SparseIntArray sectionCountArr) {
            mSectionCountArr = sectionCountArr;
            updateTypeIndex();
        }

        public int getPositionForContent(int content) {
            if (content < 0 || content > mTypeArr.size()) {
                return INVALID_POSITION;
            }
            return mContentArr.get(content);
        }

        public int getPositionForPinned(int pinned) {
            if (pinned < 0 || pinned > mTypeArr.size()) {
                return INVALID_POSITION;
            }
            return mPinnedArr.get(pinned);
        }

        public int getContentPosition(int position) {
            if (position < 0 || position > mTypeArr.size()) {
                return INVALID_POSITION;
            }

            return mContentArr.indexOfValue(position);
        }

        /**
         * get position belong to pinned section
         * @param position
         * @return
         */
        public int getPinnedPosition(int position) {
            if (position < 0 || position > mTypeArr.size()) {
                return INVALID_POSITION;
            }

            int type = getItemViewType(position);
            if (type == TYPE_PINNED) {
                return mPinnedArr.indexOfValue(position);
            } else {
                return getPinnedPosition(position - 1);
            }
        }

        private void updateTypeIndex() {
            RecyclerView mView = mWeakRef.get();
            if (null == mView) {
                return;
            }
            mTypeArr.clear();
            mContentArr.clear();
            mPinnedArr.clear();
            if (null == mSectionCountArr) {
                return;
            }
            int index = 0;
            int itemIndex = 0;
            for (int i = 0, sectionCount = mSectionCountArr.size(); i < sectionCount; i++) {
                int sectionSize = mSectionCountArr.get(i);
                mTypeArr.append(index, TYPE_PINNED);
                mPinnedArr.append(i, index);
                for (int j = 0; j < sectionSize; j++) {
                    // offset for item index
                    index++;
                    mTypeArr.append(index, TYPE_ITEM);
                    mContentArr.append(itemIndex, index);
                    itemIndex++;
                }
                // offset for section index
                index++;
            }
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (viewType == TYPE_ITEM && null != mContentAdapter) {
                return mContentAdapter.onCreateViewHolder(parent, viewType);
            } else if (viewType == TYPE_PINNED && null != mPinnedAdapter) {
                return mPinnedAdapter.onCreateViewHolder(parent, viewType);
            }
            return null;
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            int viewType = getItemViewType(position);
            if (viewType == TYPE_ITEM && null != mContentAdapter) {
                mContentAdapter.onBindViewHolder(holder, getContentPosition(position));
            } else if (viewType == TYPE_PINNED && null != mPinnedAdapter) {
                mPinnedAdapter.onBindViewHolder(holder, getPinnedPosition(position));
            }
        }

        @Override
        public int getItemCount() {
            return mTypeArr.size();
        }

        @Override
        public int getItemViewType(int position) {
            return mTypeArr.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
    }

    private class UpdateTypeObserver extends AdapterDataObserver {
        private void update() {
            if (null != mAdapter) {
                mAdapter.updateTypeIndex();
            }
        }
        @Override
        public void onChanged() {
            super.onChanged();
            update();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            super.onItemRangeChanged(positionStart, itemCount);
            update();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            super.onItemRangeChanged(positionStart, itemCount, payload);
            update();
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
            update();
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            super.onItemRangeRemoved(positionStart, itemCount);
            update();
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount);
            update();
        }
    }

    private class ScrollListener extends OnScrollListener {
        public ScrollListener() {
            super();
        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            LayoutManager manager = getLayoutManager();
            if (null == manager || null == mAdapter) {
                return;
            }
            int firstVisibleItem = -1;
            if (manager instanceof LinearLayoutManager) {
                firstVisibleItem = ((LinearLayoutManager) manager).findFirstVisibleItemPosition();
            }
            View view = manager.findViewByPosition(firstVisibleItem);
            final int width = view.getMeasuredWidth();
            final int height = view.getMeasuredHeight();

            // first Visible item belong to pinned section
            int pinnedIndex = mAdapter.getPinnedPosition(firstVisibleItem);

            // update HeadView
            if (mCurrentPinnedIndex != pinnedIndex) {
                mCurrentPinnedIndex = pinnedIndex;
                mNextPinnedPosition = mAdapter.getPositionForPinned(pinnedIndex + 1);
                ViewHolder holder = mAdapter.createViewHolder(recyclerView, PinnedWrapAdapter.TYPE_PINNED);
                mAdapter.onBindViewHolder(holder, mAdapter.getPositionForPinned(mCurrentPinnedIndex));
                mHeaderView = holder.itemView;
                measureChild(mHeaderView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
            }
            if (null == mHeaderView) {
                return;
            }
            // content next to pinned layout
            if (firstVisibleItem == mNextPinnedPosition - 1){
                final int bottom = view.getBottom();
                final int headerWidth = mHeaderView.getMeasuredWidth();
                final int headerHeight = mHeaderView.getMeasuredHeight();
                int y = bottom < headerHeight ? bottom - headerHeight : 0;
                mHeaderView.layout(0, y, headerWidth, headerHeight);
            }
            // pinned and pinned layout have some items, pinned at top
            else {
                final int headerWidth = mHeaderView.getMeasuredWidth();
                final int headerHeight = mHeaderView.getMeasuredHeight();
                mHeaderView.layout(0, 0, headerWidth, headerHeight);
            }
        }
    }

}
